{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a name=\"top\"></a><img src=\"source/SpinalHDL.png\" alt=\"SpinalHDL based on Scala\" style=\"width:320px;\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Before running Spinal HDL code, be sure to load SpinalHDL Libraries  \n",
    "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n",
    "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hierarchy\n",
    "========="
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Component \n",
    "\n",
    "Like in VHDL and Verilog, you can define components that can be used to build a design hierarchy. However, in SpinalHDL, you don’t need to bind their ports at instantiation\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Sub extends Component{\n",
    "  val a = in UInt(8 bits)\n",
    "  val b = out UInt()    \n",
    "  b :=  a\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Top extends Component{\n",
    "  val a = in UInt(8 bits)\n",
    "  val b = out UInt(8 bits) \n",
    "    \n",
    "  val u_sub = new Sub \n",
    "  u_sub.a := a\n",
    "  b := u_sub.b\n",
    "}\n",
    "showRtl(new Top) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Full adder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class AdderCell extends Component {\n",
    "  //Declaring all in/out in an io Bundle is probably a good practice\n",
    "  val io = new Bundle {\n",
    "    val a, b, cin = in Bool()\n",
    "    val sum, cout = out Bool()\n",
    "  }\n",
    "  //Do some logic\n",
    "  io.sum := io.a ^ io.b ^ io.cin\n",
    "  io.cout := (io.a & io.b) | (io.a & io.cin) | (io.b & io.cin)\n",
    "}\n",
    "\n",
    "class Adder(width: Int) extends Component {\n",
    " \n",
    "  //Create 2 AdderCell\n",
    "  val cell0 = new AdderCell\n",
    "  val cell1 = new AdderCell\n",
    "  cell1.io.cin := cell0.io.cout   //Connect cout of cell0 to cin of cell1\n",
    "\n",
    "  // Another example which create an array of ArrayCell\n",
    "  val cellArray = Array.fill(width)(new AdderCell)\n",
    "  cellArray(1).io.cin := cellArray(0).io.cout   //Connect cout of cell(0) to cin of cell(1)\n",
    " \n",
    "}\n",
    "showRtl(new Adder(8)) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Range(1,9).foldLeft(0)((a,b)=>{println(s\"$a-->$b\");b})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Input / output definition\n",
    "The syntax to define inputs and outputs is the following:\n",
    "\n",
    "Syntax | Description | Return\n",
    "-|-|-\n",
    "in/out Bool | Create an input/output Bool | Bool\n",
    "in/out Bits/UInt/SInt[(x bit)] | Create an input/output of the corresponding type | T\n",
    "in/out(T) | For all other data types, you should add the brackets around it.<br> Sorry this is a Scala limitation. | T\n",
    "master/slave(T) | This syntax is provided by the spinal.lib. T should extend IMasterSlave : <br>Some documentation is available here |T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    " class Top extends Component{\n",
    "  val a = slave Flow(UInt(8 bits))\n",
    "  val b = master Flow(UInt(8 bits)) \n",
    "  b << a //  b <> a also ok\n",
    "}\n",
    "showRtl(new Top) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are some rules to follow with component interconnection:\n",
    "\n",
    "- Components can only read output and input signals of child components\n",
    "- Components can read their own output port values (unlike VHDL)\n",
    "\n",
    "### Jump wire（飞线）\n",
    "If for some reason, you need to read signals from far away in the hierarchy (debug, temporal patch) \n",
    "you can do it by using the value returned by **`some.where.else.theSignal.pull()`**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class xxCtrl extends Component{\n",
    "  val start   = in Bool()\n",
    "  val end     = out Bool()\n",
    "  val counter = Reg(UInt(8 bits)) init 0\n",
    "  when(start){counter.clearAll}\n",
    "  .otherwise{counter := counter + 1}\n",
    "  end := counter === 255\n",
    "}\n",
    "\n",
    "class xxTop extends Component{\n",
    "    val start = in Bool()    \n",
    "    val xx = out UInt()\n",
    "    \n",
    "    val ctrl = new xxCtrl    \n",
    "    ctrl.start := start\n",
    "    \n",
    "    xx :=  ctrl.counter.pull() //Jump wire auto through IO\n",
    "}\n",
    "showRtl(new xxTop) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pruned signals\n",
    "SpinalHDL will never Pruned signals with names, for those resones:\n",
    "\n",
    "- Sometime they are used for debug purposes in the wave\n",
    "- Sometime, they should be part of the usefull RTL, but the user forgot to connect something to realy make them usefull. If they are removed from the netlist that's realy confusing to the user, and kind of hard to trace back where the missing connection is\n",
    "- It allow to design things without having the whole thing done and look at the wave already"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TopLevel extends Component { \n",
    "  val notRemoved1 = UInt(8 bits)\n",
    "  val notRemoved2 = UInt(8 bits) \n",
    "  Reg(UInt(8 bits)) init 0  //pruned signal without name without loads\n",
    "}\n",
    "showRtl(new TopLevel)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Generic(VHDL) / Parameter(Verilog)\n",
    "If you want to parameterize your component, you can give parameters to the constructor of the component as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyAdder(width: BitCount) extends Component {\n",
    "  val io = new Bundle{\n",
    "    val a,b    = in UInt(width)\n",
    "    val result = out UInt(width)\n",
    "  }\n",
    "  io.result := io.a + io.b\n",
    "}\n",
    "\n",
    "showRtl(new MyAdder(8 bits))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I you have several parameters, it is a good practice to give a specific configuration class as follows:\n",
    "```scala\n",
    "case class MySocConfig(axiFrequency  : HertzNumber,\n",
    "                       onChipRamSize : BigInt,\n",
    "                       cpu           : RiscCoreConfig,\n",
    "                       iCache        : InstructionCacheConfig)\n",
    "\n",
    "class MySoc(config: MySocConfig) extends Component {\n",
    "    ...\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function\n",
    "The ways you can use Scala functions to generate hardware are radically different than VHDL/Verilog for many reasons:\n",
    "\n",
    "- You can instantiate registers, combinatorial logic and components inside them.\n",
    "\n",
    "- You don’t have to play with process/@always that limit the scope of assignment of signals\n",
    "\n",
    "- Everything is passed by reference, which allows easy manipulation.\n",
    "For example you can give a bus to a function as an argument, then the function can internaly read/write to it.\n",
    "You can also return a Component, a Bus, or anything else from scala and the scala world."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Component whith function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Top extends Component{\n",
    "  val a = in UInt(8 bits)\n",
    "  val b = out UInt(8 bits) \n",
    "  val c = out UInt(8 bits) \n",
    "    \n",
    "  def pass(x: UInt, n : Int) = {\n",
    "      val ret = UInt(n bits)\n",
    "          ret := x \n",
    "      ret \n",
    "  }\n",
    "    \n",
    "  def pass2(x: UInt) = {\n",
    "      class Fix(n: Int) extends Component {\n",
    "          val a = in UInt()\n",
    "          val b = out  UInt() \n",
    "          b := pass(in(a), n)\n",
    "      }\n",
    "      val res = new Fix(x.getWidth)\n",
    "      res.a := x\n",
    "      res.b\n",
    "  }\n",
    "    b := pass(a,8)\n",
    "    c := pass2(a)\n",
    "}\n",
    "showRtl(new Top) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### RGB to gray\n",
    "For example if you want to convert a Red/Green/Blue color into greyscale by using coefficients, you can use functions to apply them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Top extends Component{\n",
    "    // Input RGB color\n",
    "val r, g, b = UInt(8 bits)\n",
    "\n",
    "// Define a function to multiply a UInt by a scala Float value.\n",
    "def coef(value: UInt, by: Float): UInt = (value * U((255*by).toInt, 8 bits) >> 8)\n",
    "\n",
    "// Calculate the gray level\n",
    "val gray = coef(r, 0.3f) + coef(g, 0.4f) + coef(b, 0.3f)\n",
    "}\n",
    "showRtl(new Top) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Valid Ready Payload bus\n",
    "For instance if you define a simple Valid Ready Payload bus, you can then define some useful functions inside of it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class MyBus(payloadWidth: Int) extends Bundle with IMasterSlave {\n",
    "  val valid   = Bool()\n",
    "  val ready   = Bool()\n",
    "  val payload = Bits(payloadWidth bits)\n",
    "\n",
    "  // define the direction of the data in a master mode\n",
    "  override def asMaster(): Unit = {\n",
    "    out(valid, payload)\n",
    "    in(ready)\n",
    "  }\n",
    "\n",
    "  // Connect that to this\n",
    "  def <<(that: MyBus): Unit = {\n",
    "    this.valid   := that.valid\n",
    "    that.ready   := this.ready\n",
    "    this.payload := that.payload\n",
    "  }\n",
    "\n",
    "  // Connect this to the FIFO input, return the fifo output\n",
    "  def queue(size: Int): MyBus = {\n",
    "    val fifo = new MyBusFifo(payloadWidth, size)\n",
    "    fifo.io.push << this\n",
    "    return fifo.io.pop\n",
    "  }\n",
    "}\n",
    "\n",
    "class MyBusFifo(payloadWidth: Int, depth: Int) extends Component {\n",
    "  val io = new Bundle {\n",
    "    val push = slave(MyBus(payloadWidth))\n",
    "    val pop  = master(MyBus(payloadWidth))\n",
    "  }\n",
    " io.pop <> io.push\n",
    "}\n",
    "\n",
    "class Top extends Component {\n",
    "  val io = new Bundle {\n",
    "    val idata = slave(MyBus(8))\n",
    "    val odata  = master(MyBus(8))\n",
    "  }\n",
    "  io.odata << io.idata.queue(32)\n",
    "}\n",
    "showRtl(new Top)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Area\n",
    "Sometimes, creating a Component to define some logic is overkill because you:   \n",
    "\n",
    "- Need to define all construction parameters and IO (verbosity, duplication)\n",
    "- Split your code (more than needed)\n",
    "\n",
    "For this kind of case you can use an Area to define a group of signals/logic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UartCtrl extends Component {\n",
    "  \n",
    "  val timer = new Area {\n",
    "    val counter = Reg(UInt(8 bit))\n",
    "    val tick = counter === 0\n",
    "    counter := counter - 1\n",
    "    when(tick) {\n",
    "      counter := 100\n",
    "    }\n",
    "  }\n",
    "\n",
    "  val tickCounter = new Area {\n",
    "    val value = Reg(UInt(3 bit))\n",
    "    val reset = False\n",
    "    when(timer.tick) {          // Refer to the tick from timer area\n",
    "      value := value + 1\n",
    "    }\n",
    "    when(reset) {\n",
    "      value := 0\n",
    "    }\n",
    "  }\n",
    "\n",
    "  val stateMachine = new Area {\n",
    "   \n",
    "  }\n",
    "}\n",
    "showRtl(new UartCtrl)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In VHDL and Verilog, sometimes prefixes are used to separate variables into logical sections.  \n",
    "It is suggested that you use `Area` instead of this in SpinalHDL.  \n",
    "\n",
    "[ClockingArea](https://spinalhdl.github.io/SpinalDoc-RTD/SpinalHDL/Structuring/clock_domain.html#clock-domain) is a special kind of `Area` that allows you to define chunks of hardware which use a given `ClockDomain`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example: Parameter Pipline AdderTree \n",
    "**requirement**:\n",
    "\n",
    "- VectorSize Configurable\n",
    "- GroupSize Configurable, auto Group\n",
    "- DataWidth Configurable\n",
    "\n",
    "![adderTree1](./source/addertree1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1st: creat Sum-Component which adder all Vec input together in one cycle with Register out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Sum(diw: Int, size: Int, stage: Int) extends Component{\n",
    "    this.setDefinitionName(s\"sum_stage${stage}_n${size}_w${diw}\")\n",
    "    val dow = diw + log2Up(size)\n",
    "\n",
    "    val io = new Bundle{\n",
    "      val nets = slave Flow(Vec(SInt(diw bits), size))\n",
    "      val sum  = out(SInt(dow bits)).setAsReg()\n",
    "    }\n",
    "\n",
    "    when(io.nets.valid){\n",
    "      io.sum := io.nets.payload\n",
    "        .map(_.resize(dow bits))\n",
    "        .reduce(_ + _)\n",
    "    }\n",
    "}\n",
    "showRtl(new Sum(8, 2, 0))\n",
    "showRtl(new Sum(8, 3, 0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2st: Creating recursive functions\n",
    "Auto Sum together with pipeline divide by Group-size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "private def sumAdd(nets: Flow[Vec[SInt]], stage: Int): Sum = {\n",
    "    val uSum = new Sum(nets.payload.head.getWidth, nets.payload.size, stage)\n",
    "    uSum.io.nets.valid   := nets.valid\n",
    "    uSum.io.nets.payload := nets.payload.resized\n",
    "    uSum\n",
    "  }\n",
    "\n",
    "def pipeTree(nets: Flow[Vec[SInt]], groupSize: Int , stage: Int = 0): (List[Sum], Int) = {\n",
    "\n",
    "    val nextstage = stage + 1\n",
    "\n",
    "    if (nets.payload.size <= groupSize) {\n",
    "      (List(sumAdd(nets, nextstage)), nextstage)\n",
    "    } else {\n",
    "      val grpNum = scala.math.ceil(nets.payload.size.toDouble / groupSize).toInt\n",
    "      val nextStage = (0 until grpNum).toList\n",
    "        .map(i => nets.payload.drop(i * groupSize).take(groupSize))\n",
    "        .map{ grouped =>\n",
    "          val groupednets = Flow(Vec(SInt(grouped.head.getWidth bits), grouped.size))\n",
    "          groupednets.valid   := nets.valid\n",
    "          groupednets.payload := Vec(grouped)\n",
    "          sumAdd(groupednets, nextstage)\n",
    "        }\n",
    "      val ret = Flow(Vec(SInt(nextStage.head.io.sum.getWidth bits), nextStage.size))\n",
    "      ret.valid   := RegNext(nets.valid, init = False)\n",
    "      ret.payload := Vec(nextStage.map(_.io.sum)).resized\n",
    "      pipeTree(ret, groupSize, nextstage)\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3st[option]: given adder tree with a component Top"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "class AdderTree(diw: Int, size: Int, groupSize: Int) extends Component{\n",
    "\n",
    "    val io_nets = slave Flow(Vec(SInt(diw bits), size))\n",
    "\n",
    "    val (sum, stage) = pipeTree(io_nets, groupSize, 0)\n",
    "\n",
    "    this.setDefinitionName(s\"adderTree_n${size}_g${groupSize}_dly${stage}\")\n",
    "\n",
    "    def Latency: Int = stage\n",
    "\n",
    "    def dow: Int = diw + log2Up(groupSize) * stage\n",
    "\n",
    "    val io_sum  = master Flow(SInt(sum.head.io.sum.getWidth bits))\n",
    "\n",
    "    io_sum.payload := sum.head.io.sum\n",
    "    io_sum.valid   := RegNext(sum.head.io.nets.valid, init = False)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4st[option]: Provides a simple common function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "object AdderTree {\n",
    "  def apply(nets: Flow[Vec[SInt]], addCellSize: Int): AdderTree = {\n",
    "    val uAdderTree = new AdderTree(nets.payload.head.getWidth, nets.payload.size, addCellSize)\n",
    "    uAdderTree.io_nets := nets\n",
    "    uAdderTree\n",
    "  }\n",
    "\n",
    "  def apply(nets: Vec[SInt], addCellSize: Int): AdderTree = {\n",
    "    val uAdderTree = new AdderTree(nets.head.getWidth, nets.size, addCellSize)\n",
    "    uAdderTree.io_nets.payload := nets\n",
    "    uAdderTree.io_nets.valid   := True\n",
    "    uAdderTree\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Usage:\n",
    "```scala\n",
    "class Top extends Component{\n",
    "  val nets = Flow(Vec(SInt(8 bits), 23))\n",
    "  AdderTree(nets, 2)//group size = 2\n",
    "}\n",
    "showRtl(new Top)\n",
    "```\n",
    "this will generator diagram [below](http://localhost:8888/notebooks/2.6-Spinal-core-Component-Function-Area.ipynb#Example:-Parameter-Pipline-AdderTree) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Top extends Component{\n",
    "    val nets = Flow(Vec(SInt(8 bits), 23))\n",
    "    AdderTree(nets, addCellSize = 4)//group size = 4\n",
    "}\n",
    "\n",
    "showRtl(new Top)\n",
    "// we change the addCellSize/groupSize = 4 , then got following diagram  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![adderTree2](./source/addertree2.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Scala",
   "language": "scala",
   "name": "scala"
  },
  "language_info": {
   "codemirror_mode": "text/x-scala",
   "file_extension": ".scala",
   "mimetype": "text/x-scala",
   "name": "scala",
   "nbconvert_exporter": "script",
   "version": "2.11.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
